//
//  BleManager.m
//  bleDemo
//
//  Created by zt on 2020/4/13.
//  Copyright © 2020 XB. All rights reserved.
//

/*
 小白
 1 外设调用连接 要不要考虑强引用？
 2 设置外设的代理
 3 验证~~~~连接成功、失败、断开连接是不是会回调到这里的代理方法
 4 发送数据的应该在外设的代理方法
 */

#import "BleManager.h"


static BleManager   *manager = nil;

@interface BleManager ()


/// 中心
@property (nonatomic, strong) CBCentralManager    *centralManager;
/// 外设
//@property (nonatomic, strong) CBPeripheral        *peripheral;
/// 当前的外设
@property (nonatomic, strong) BleDevice *currentDevice;

//@property (nonatomic, weak) id <BLEManagerDelegate> delegate;
/// 扫描代理
@property (nonatomic, weak) id <FSCentralScanDelegate>    centralDelegate;
/// 连接代理
@property (nonatomic, weak) id <FSCentralConnectDelegate> connectDelegate;
/// 连接次数
@property (nonatomic, assign) NSInteger connectCount;
/// 连接时间
@property (nonatomic, assign) NSInteger connectTimeInterval;
/// 当前连接次数
@property (nonatomic, assign) NSInteger currentNumberOfConnect;

@end

@implementation BleManager



#pragma mark  生命周期
- (instancetype)initWithDelegate:(id <FSCentralScanDelegate>)delegate {
    if (self = [super init]) {
        if (delegate) {
            // 5月24日 代理要如何设备比较合理，这个地方要仔细想想才开始修改
            self.centralDelegate = delegate;
            self.connectCount = 1;
            self.scanTimeInterval = 30;
            [self.centralManager scanForPeripheralsWithServices:nil options:nil];
        }
    }
    return self;
}

#pragma mark 开放方法
+ (BleManager *)managerWithDelegate:(id <FSCentralScanDelegate>)delegate {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (manager == nil) {
            manager = [[self alloc] initWithDelegate:delegate];
        }
    });
    return manager;
}

+ (BleManager *)manager {
    return manager;
}




- (BOOL)startScan {
    // 蓝牙开关关闭，直接返回NO
    if (self.state != BleManagerStatePoweredOn) return NO;
    // 清空管理中的蓝牙设备
    [self.devices removeAllObjects];
    // 代理如果实现了指定扫描服务，先执行代理方法，在扫描指定服务
    if ([self.centralDelegate respondsToSelector:@selector(manager:willScanWithServices:)] && self.centralDelegate) {
        [self.centralDelegate manager:self willScanWithServices:self.filterServices];
        // 需要过去的服务，处于性能考虑，正常是要实现这个方法
        [self.centralManager scanForPeripheralsWithServices:self.filterServices options:nil];
    } else {
        // 没有需要过去的服务  扫描全部蓝牙外设
        [self.centralManager scanForPeripheralsWithServices:nil options:nil];
    }
    // 指定时间后，停止扫描
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.scanTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.centralManager stopScan];
    });
    return YES;
}

- (void)stopScan {
    // 停止扫描
    [self.centralManager stopScan];
}

- (void)clean {

}

- (void)disconnect:(BleDevice *)device {
    [self.centralManager cancelPeripheralConnection:device.peripheral];
    if (device.delegate && [device.delegate respondsToSelector:@selector(device:didDisconnectedWithMode:)]) {
        [device.delegate device:device didDisconnectedWithMode:BleDisconnectModeManual];
    }
}
- (void)connect:(BleDevice *)device {
    PLog(@"发起连接");
//    device.connectCount = 0;
    self.connectTimeInterval = 2;
    self.currentNumberOfConnect = 1;
    if (self.connectDelegate &&
        [self.connectDelegate respondsToSelector:@selector(singleConnectTimeInterval)]) {
        self.connectTimeInterval = [self.connectDelegate singleConnectTimeInterval];
    }

    if (self.connectDelegate &&
        [self.connectDelegate respondsToSelector:@selector(maxNumberOfConnentCounts)]) {
        self.connectCount = [self.connectDelegate maxNumberOfConnentCounts];
    }

    
    [self.centralManager connectPeripheral:device.peripheral options:nil];
}

//- (void)disconnect {
//    [self.centralManager cancelPeripheralConnection:self.currentDevice.peripheral];
//    // 通过代理回调 用户主动断开蓝牙连接
//    if (self.currentDevice.delegate &&
//        [self.currentDevice.delegate respondsToSelector:@selector(device:didDisconnectedWithMode:)]) {
//        [self.currentDevice.delegate device:self.currentDevice didDisconnectedWithMode:BleDisconnectModeManual];
//    }
//}

#pragma mark 系统蓝牙代理相关
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBManagerStatePoweredOn: { // 只有这个状态才可以使用
            [self setValue:@(BleManagerStatePoweredOn) forKey:@"state"];
        }
            break;
        case CBManagerStatePoweredOff: { // 蓝牙未开启  至于要不要开启蓝牙开关，这个要使用者自己决定
            [self setValue:@(BleManagerStatePoweredOff) forKey:@"state"];
        }
            break;

        default: { // 其他状态都返回蓝牙不支持，省事
            [self setValue:@(BleManagerStateUnsupported) forKey:@"state"];
        }
            break;
    }
    // 判断代理是否实现方法
    if (self.centralDelegate && [self.centralDelegate respondsToSelector:@selector(manager:didUpdateState:)]) {
        [self.centralDelegate manager:self didUpdateState:self.state];
    }

}
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict {
    PLog(@"~~~~~~~注意~~~~~~~~这个方法什么是时候调用，程序挂起，还是什么时候调用，应该百度看看");
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    if (!peripheral.name) { // 外设的名字为空，直接返回
        return;
    }
    // 发现新外设
    BleDevice *new_device = [BleDevice new];
    [new_device setValue:peripheral forKey:@"peripheral"];
    [new_device setValue:advertisementData forKey:@"advertisementData"];
    [new_device setValue:RSSI forKey:@"RSSI"];
    [new_device setValue:peripheral.name forKey:@"name"];

    // 先询问代理 是否需要过滤外设，如果需要过滤外设，先过滤外设
    if ([self.centralDelegate respondsToSelector:@selector(manager:willDiscoverDevice:)] && self.centralDelegate) {
        if ([self.centralDelegate manager:self willDiscoverDevice:new_device]) { // 要加入到外设容器
            // 添加外设到蓝牙管理器中
            [self addBleDeviceToArray:new_device];
            if ([self.centralDelegate respondsToSelector:@selector(manager:didDiscoverDevice:)] && self.centralDelegate) { // 如果代理有实现这个方法，
                [self.centralDelegate manager:self didDiscoverDevice:new_device];
            }
        }
        return;
    } else { // 没有做 过滤  直接把外设加入到外设数组的容器中
        [self addBleDeviceToArray:new_device];
        if ([self.centralDelegate respondsToSelector:@selector(manager:didDiscoverDevice:)] && self.centralDelegate) { // 如果代理有实现这个方法，
            [self.centralDelegate manager:self didDiscoverDevice:new_device];
        }
    }
}

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    // 连接成功
    PLog(@"连接成功 %@", NSStringFromSelector(_cmd));
//    self.currentDevice.connected
    [self.currentDevice setValue:@(1) forKey:@"connected"];
    [self.currentDevice setValue:@(0) forKey:@"connecting"];
    // 回调连接成功
    if (self.currentDevice.delegate &&
        [self.currentDevice.delegate respondsToSelector:@selector(deviceDidConnected:)]) {
        [self.currentDevice.delegate deviceDidConnected:self.currentDevice];
    }
    [self.currentDevice.peripheral discoverServices:nil];
}

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    // 连接超时 用户主动断开，都会通代理回调，如果是蓝牙状态发送改变，都回调蓝牙关闭了
    if (self.state != BleManagerStatePoweredOn &&
        self.currentDevice.delegate &&
        [self.currentDevice.delegate respondsToSelector:@selector(device:didDisconnectedWithMode:)]) {
        [self.currentDevice.delegate device:self.currentDevice didDisconnectedWithMode:BleDisconnectModePoweredOff];
    }
}

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    // 连接失败重新连接
    [self.currentDevice setValue:@(0) forKey:@"connected"];
    [self connectAction];
}

#pragma mark 私有方法
/// 设备加入到管理器中  如果是新的直接加入，如果是旧的，把旧的更新为新传入的设备
/// @param device 外设
- (void)addBleDeviceToArray:(BleDevice *)device {
    // 添加蓝牙设备
    // 判断设备是不是新的，如果设备是新的，直接添加，如果是旧点就直接替换
    // 因为这里应该更新蓝牙的信号强度，用户在连接蓝牙外设的时候，只关心能不能连上，其他专业的信息其实并不关系，也不用关心
    if (!self.devices.count) { // 没有数据数据直接加
        [self.devices addObject:device];
    }
    for (int i = 0; i <= self.devices.count; i++) {
        // 找到最后一个都没有找到，说明这个设备是新设备，加入到数组中去
        if (i == self.devices.count) {
            [self.devices addObject:device];
        }
        BleDevice *old = self.devices[i];
        // 旧的设备和新的设备名字不用，继续找
        if ([old.name isEqualToString:device.name]) continue;
        // 设备已经存在，更新设备
        [self.devices replaceObjectAtIndex:i withObject:device];
    }
}

/// 连接动作，失败就重新连接
- (void)connectAction {
    int connectTimeInterval = 10;

    // 指定时间回调事件
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(connectTimeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 连接成功，在中心的代理已经回调了， 这里直接返回就行
        if (self.currentDevice.connected) return;

        /*
         连接失败：
         1 没有超过指定次数》连接次数加1》发起重新连接
         2 超过指定次数》回调连接超时
         */
        if (self.connectCount > 1) {
            // 断开连接
            [self.centralManager cancelPeripheralConnection:self.currentDevice.peripheral];
            [self.currentDevice setValue:@(0) forKey:@"connecting"];
            if (self.currentDevice.delegate &&
                [self.currentDevice respondsToSelector:@selector(device:didDisconnectedWithMode:)]) {
                // 代理回调
                [self.currentDevice.delegate device:self.currentDevice didDisconnectedWithMode:BleDisconnectModeTimeout];
            }
            return ;
        } else {
            self.connectCount++;
            [self.currentDevice setValue:@(self.connectCount) forKey:@"connectCount"];
            [self.centralManager connectPeripheral:self.currentDevice.peripheral options:nil];
        }
    });
}

#pragma mark setter & getter
- (NSMutableArray<CBUUID *> *)filterServices {
    if (!_filterServices) {
        _filterServices = [NSMutableArray new];
    }
    return _filterServices;
}

- (NSMutableArray *)devices {
    if (_devices) {
        _devices = [NSMutableArray new];
    }
    return _devices;
}

- (CBCentralManager *)centralManager {
    if (!_centralManager) {
        // 初始化这个会可以返回系统蓝牙开关的状态
        _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    }
    return _centralManager;
}

@end
